package workflow;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JOptionPane;

import model.Software;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseConfiguration;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.conf.EventProcessingOption;
import org.drools.definition.KnowledgePackage;
import org.drools.io.ResourceFactory;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.KnowledgeSessionConfiguration;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.conf.ClockTypeOption;

import workerUIs.SpecificationStepFrame;
import workerUIs.StepFrame;
import workers.ArchitectureStepWorker;
import workers.ImplementationStepWorker;
import workers.IntegrationStepWorker;
import workers.ReleaseStepWorker;
import workers.RepairStepWorker;
import workers.SpecificationStepWorker;
import workers.TestStepWorker;
import workers.UIStepWorker;

/**
 * SoftwareDevelopmentWorkflow class. 
 * Represents the main class in the Application.
 * 
 * @author Mark Czotter
 * @author Miklos Szennai
 * 
 */
public class SoftwareDevelopmentWorkflow implements IEventListener {
	
	private Logger logger = Logger.getLogger(SoftwareDevelopmentWorkflow.class.getCanonicalName());
	
    private KnowledgeBuilder kbuilder;
    private Collection<KnowledgePackage> pkgs;
    private KnowledgeBase kbase;
    private StatefulKnowledgeSession ksession;
    //private WorkingMemoryEntryPoint entryPoint1;
    private KnowledgeRuntimeLogger kLogger;

    // configuration
	public static boolean automaticMode = true;
	public static boolean oneSidedDecision = true;
	Random test = new Random();
	boolean prevTest = false;

	private ExecutorService executor;

	public SoftwareDevelopmentWorkflow() {
		executor = Executors.newCachedThreadPool();
	}
	
	/**
	 * Starts the development immediately.
	 */
	public void startDevelopment() {
		try {
			// create all blockingQueues
			final BlockingQueue<Software> archiInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> implInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> uiInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> implOutputintegrationInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> uiOutputIntegrationInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> testInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> repairStepInputQueue = new ArrayBlockingQueue<Software>(10);
			final BlockingQueue<Software> releaseStepInputQueue = new ArrayBlockingQueue<Software>(10);
			// create all frames backwards from release to spec, and add actionListeners to buttons
			final StepFrame releaseFrame = new StepFrame("Step5: Release", 400, 600);
			releaseFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					executor.execute(new ReleaseStepWorker(releaseFrame, releaseStepInputQueue, SoftwareDevelopmentWorkflow.this));
				}
			});
			final StepFrame repairFrame = new StepFrame("Step4e: Repair", 800, 600);
			repairFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					executor.execute(new RepairStepWorker(repairFrame, repairStepInputQueue, releaseStepInputQueue, releaseFrame, SoftwareDevelopmentWorkflow.this));
				}
			});
			final StepFrame testFrame = new StepFrame("If block: Testing", 1200, 600);
			testFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					boolean testOk = false;
					if (automaticMode) {
						testOk = true;
						if(oneSidedDecision)
						{
							testOk = true;
						}
						else
						{
							if(prevTest)
								testOk = false;
							else
								testOk = true;
							prevTest = !prevTest;
						}
					} else {
						switch (JOptionPane.showConfirmDialog(testFrame, "Tests succcessful?", "Tests succcessful?", JOptionPane.YES_NO_OPTION)) {
						case JOptionPane.YES_OPTION: 
							testOk = true;
							break;
						default: 
							testOk = false;
							break;
						}	
					}
					executor.execute(new TestStepWorker(testFrame, testInputQueue, repairStepInputQueue, releaseStepInputQueue, repairFrame, releaseFrame, testOk, SoftwareDevelopmentWorkflow.this));					
				}
			});
			final StepFrame integrationFrame = new StepFrame("Step4: Integration", 1200, 200);
			integrationFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					integrationFrame.getNextButton().setEnabled(false);
					executor.execute(new IntegrationStepWorker(integrationFrame, implOutputintegrationInputQueue, uiOutputIntegrationInputQueue, testInputQueue, testFrame, SoftwareDevelopmentWorkflow.this));				
				}
			});
			final StepFrame uiImplFrame = new StepFrame("Step3b: UI Impl", 800, 400);
			uiImplFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					executor.execute(new UIStepWorker(uiImplFrame, uiInputQueue, uiOutputIntegrationInputQueue, integrationFrame, SoftwareDevelopmentWorkflow.this));				
				}
			});
			final StepFrame implementationFrame = new StepFrame("Step3a: Implementation", 800,0);
			implementationFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					executor.execute(new ImplementationStepWorker(implementationFrame, implInputQueue, implOutputintegrationInputQueue, integrationFrame, SoftwareDevelopmentWorkflow.this));				
				}
			});
			final StepFrame architectureFrame = new StepFrame("Step2: Architecure Design", 400, 200);
			architectureFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					executor.execute(new ArchitectureStepWorker(architectureFrame, archiInputQueue, implInputQueue, uiInputQueue, implementationFrame, uiImplFrame, SoftwareDevelopmentWorkflow.this));				
				}
			});
			final SpecificationStepFrame specFrame = new SpecificationStepFrame("Step1: Specification", 0, 200);
			specFrame.getNextButton().addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					executor.execute(new SpecificationStepWorker(specFrame, archiInputQueue, architectureFrame, SoftwareDevelopmentWorkflow.this));
				}
			});
		} catch (Exception e) {
			logger.log(Level.SEVERE, "Error during initialization: ", e);
		}
	}
	
	private void initDrools() {
    	kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("ruleset.drl", SoftwareDevelopmentWorkflow.class), ResourceType.DRL);

        if (kbuilder.hasErrors()) {
            System.out.println(kbuilder.getErrors().toString());
            throw new RuntimeException("Unable to compile drl\".");
        }
        pkgs = kbuilder.getKnowledgePackages();

        KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
        config.setOption(EventProcessingOption.STREAM);		//STREAM mode option
        kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase = KnowledgeBaseFactory.newKnowledgeBase(config);		//set KnowledgeBase into STREAM mode

        kbase.addKnowledgePackages(pkgs);

        KnowledgeSessionConfiguration conf = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
        conf.setOption(ClockTypeOption.get("realtime"));
        ksession = kbase.newStatefulKnowledgeSession(conf,null);	//New Stateful Session
        kLogger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/logger");

        //entryPoint1 = ksession.getWorkingMemoryEntryPoint("entryone");	//get the WM Entry Point

    }

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		SoftwareDevelopmentWorkflow sw = new SoftwareDevelopmentWorkflow();
		sw.initDrools();
		sw.startDevelopment();
//		sw.dispose();
	}
	
	private void dispose() {
		kLogger.close();
		ksession.dispose();
	}

	@Override
	public void onEvent(WorkflowEvent e) {
//		System.out.println(e);
		try {
			if (ksession != null) {
				ksession.insert(e);
				ksession.fireAllRules();
			}
		} catch (Exception e2) {
			logger.log(Level.SEVERE, "Exception during event processing in drools! \n");
			e2.printStackTrace();
		}
	}

}
